package com.midea.ai.air.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.GradientDrawable;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewParent;

import com.midea.ai.air.R;

public class SlideButton extends View {
    private Paint mPaint;

    private ViewParent mParent;

    private float mFirstDownY;

    private float mFirstDownX;

    private float mThumbPos;

    private float mThumbOnPos;

    private float mThumbOffPos;

    private float mThumbInitPos;

    private int mClickTimeout;

    private int mTouchSlop;

    private boolean mChecked = false;

    private boolean mTurningOn;

    private PerformClick mPerformClick;

    private OnCheckedChangeListener mOnCheckedChangeListener;

    private boolean mAnimating;

    private final float VELOCITY = 350;

    private float mVelocity;

    private float mAnimationPosition;

    private float mAnimatedVelocity;

    private int defaultColor = getResources().getColor(
            R.color.background_slidebutton_default);
    private int selectedColor = getResources().getColor(
            R.color.background_slidebutton_selected);
    private int thumbColor = getResources().getColor(
            R.color.background_thumb_slidebutton);
    private int strokeColor = getResources().getColor(
            R.color.background_slidebutton_stroke);

    private GradientDrawable backgroundDefault = new GradientDrawable();
    private GradientDrawable backgroundSelected = new GradientDrawable();

    private int widthDip = 50;
    private int heightDip = 30;
    private int thumbWidthDip = 30;
    private int cornerRadiusDip = 15;
    private float strokeDip = 0.2f;

    private int width;
    private int height;
    private int thumbWidth;
    private int cornerRadius;
    private int stroke;

    private float[] leftRadii;
    private float[] rightRadii;
    private float[] wholeRadii;

    public SlideButton(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SlideButton(Context context) {
        this(context, null);
    }

    public SlideButton(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initView(context);
    }

    private void initView(Context context) {
        mPaint = new Paint();
        // get viewConfiguration
        mClickTimeout = ViewConfiguration.getPressedStateDuration()
                + ViewConfiguration.getTapTimeout();
        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();

        float density = getResources().getDisplayMetrics().density;
        mVelocity = (int) (VELOCITY * density + 0.5f);
        width = dip2px(context, widthDip);
        height = dip2px(context, heightDip);
        thumbWidth = dip2px(context, thumbWidthDip);
        cornerRadius = dip2px(context, cornerRadiusDip);
        stroke = dip2px(context, strokeDip);

        mThumbOffPos = 0;
        mThumbOnPos = width - thumbWidth;
        mThumbPos = mChecked ? mThumbOnPos : mThumbOffPos;

        leftRadii = new float[]{cornerRadius, cornerRadius, 0, 0, 0, 0,
                cornerRadius, cornerRadius};
        rightRadii = new float[]{0, 0, cornerRadius, cornerRadius,
                cornerRadius, cornerRadius, 0, 0};
        wholeRadii = new float[]{cornerRadius, cornerRadius, cornerRadius,
                cornerRadius, cornerRadius, cornerRadius, cornerRadius,
                cornerRadius};

        backgroundDefault.setColor(defaultColor);
        backgroundDefault.setStroke(stroke, strokeColor);

        backgroundSelected.setColor(selectedColor);
        backgroundSelected.setStroke(stroke, strokeColor);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (mAnimating
                || (!mAnimating && mThumbPos > mThumbOffPos && mThumbPos < mThumbOnPos)) {
            // draw default
            backgroundDefault.setCornerRadii(rightRadii);
            backgroundDefault.setBounds((int) (mThumbPos + thumbWidth / 2f), 0,
                    width, height);
            backgroundDefault.draw(canvas);
            // draw selected
            backgroundSelected.setCornerRadii(leftRadii);
            backgroundSelected.setBounds(0, 0,
                    (int) (mThumbPos + thumbWidth / 2f), height);
            backgroundSelected.draw(canvas);
        } else {
            if (mThumbPos == mThumbOnPos) {
                backgroundSelected.setCornerRadii(wholeRadii);
                backgroundSelected.setBounds(0, 0, width, height);
                backgroundSelected.draw(canvas);
            } else if (mThumbPos == mThumbOffPos) {
                backgroundDefault.setCornerRadii(wholeRadii);
                backgroundDefault.setBounds(0, 0, width, height);
                backgroundDefault.draw(canvas);
            }
        }
        // draw thumb
        mPaint.setColor(thumbColor);
        canvas.drawCircle(mThumbPos + thumbWidth / 2f, thumbWidth / 2f,
                thumbWidth / 2f - dip2px(getContext(), 3), mPaint);
    }

    @Override
    public void setEnabled(boolean enabled) {
        super.setEnabled(enabled);
    }

    public boolean isChecked() {
        return mChecked;
    }

    public void toggle() {
        setChecked(!mChecked);
    }

    /**
     * <p>
     * Changes the checked state of this button.
     * </p>
     *
     * @param checked true to check the button, false to uncheck it
     */
    public void setChecked(boolean checked) {
        if (mChecked != checked) {
            mChecked = checked;

            mThumbPos = checked ? mThumbOnPos : mThumbOffPos;
            invalidate();

            if (mOnCheckedChangeListener != null) {
                mOnCheckedChangeListener.onCheckedChanged(SlideButton.this,
                        mChecked);
            }
        }
    }

    /**
     * 此方法用于不处理非用户行为的动作
     *
     * @param checked
     */
    public void setCheckedNoUser(boolean checked) {
        if (mChecked != checked) {
            mChecked = checked;

            mThumbPos = checked ? mThumbOnPos : mThumbOffPos;
            invalidate();

//			if (mOnCheckedChangeListener != null)
//			{
//				mOnCheckedChangeListener.onCheckedChanged(SlideButton.this,
//						mChecked);
//			}
        }
    }

    /**
     * Register a callback to be invoked when the checked state of this button
     * changes.
     *
     * @param listener the callback to call on checked state change
     */
    public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
        mOnCheckedChangeListener = listener;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        float x = event.getX();
        float y = event.getY();
        float deltaX = Math.abs(x - mFirstDownX);
        float deltaY = Math.abs(y - mFirstDownY);
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                attemptClaimDrag();
                mFirstDownX = x;
                mFirstDownY = y;
                mThumbInitPos = mChecked ? mThumbOnPos : mThumbOffPos;
                break;
            case MotionEvent.ACTION_MOVE:
                mThumbPos = mThumbInitPos + event.getX() - mFirstDownX;
                if (mThumbPos <= mThumbOffPos) {
                    mThumbPos = mThumbOffPos;
                }
                if (mThumbPos >= mThumbOnPos) {
                    mThumbPos = mThumbOnPos;
                }
                mTurningOn = mThumbPos > (mThumbOnPos - mThumbOffPos) / 2
                        + mThumbOffPos;

                break;
            case MotionEvent.ACTION_UP:
                float time = event.getEventTime() - event.getDownTime();
                if (deltaY < mTouchSlop && deltaX < mTouchSlop
                        && time < mClickTimeout) {
                    if (mPerformClick == null) {
                        mPerformClick = new PerformClick();
                    }
                    if (!post(mPerformClick)) {
                        performClick();
                    }
                } else {
                    startAnimation(mTurningOn);
                }
                break;
        }

        invalidate();
        return isEnabled();
    }

    private final class PerformClick implements Runnable {
        public void run() {
            performClick();
        }
    }

    @Override
    public boolean performClick() {
        startAnimation(!mChecked);
        return true;
    }

    /**
     * Tries to claim the user's drag motion, and requests disallowing any
     * ancestors from stealing events in the drag.
     */
    private void attemptClaimDrag() {
        mParent = getParent();
        if (mParent != null) {
            mParent.requestDisallowInterceptTouchEvent(true);
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(width, height);
    }

    private void startAnimation(boolean turnOn) {
        mAnimating = true;
        mAnimatedVelocity = turnOn ? mVelocity : -mVelocity;
        mAnimationPosition = mThumbPos;

        new SwitchAnimation().run();
    }

    private void stopAnimation() {
        mAnimating = false;
    }

    private final class SwitchAnimation implements Runnable {

        @Override
        public void run() {
            if (!mAnimating) {
                return;
            }
            doAnimation();
            requestAnimationFrame(this);
        }
    }

    private void doAnimation() {
        mAnimationPosition += mAnimatedVelocity * ANIMATION_FRAME_DURATION
                / 1000;
        if (mAnimationPosition >= mThumbOnPos) {
            setChecked(true);
            stopAnimation();
            mAnimationPosition = mThumbOnPos;
        } else if (mAnimationPosition <= mThumbOffPos) {
            setChecked(false);
            stopAnimation();
            mAnimationPosition = mThumbOffPos;
        }
        moveView(mAnimationPosition);
    }

    private static final int MSG_ANIMATE = 1000;

    public static final int ANIMATION_FRAME_DURATION = 1000 / 60;

    private static final Handler mHandler = new AnimationHandler();

    private void requestAnimationFrame(Runnable runnable) {
        Message message = new Message();
        message.what = MSG_ANIMATE;
        message.obj = runnable;
        mHandler.sendMessageDelayed(message, ANIMATION_FRAME_DURATION);
    }

    private static class AnimationHandler extends Handler {
        public void handleMessage(Message m) {
            switch (m.what) {
                case MSG_ANIMATE:
                    if (m.obj != null) {
                        ((Runnable) m.obj).run();
                    }
                    break;
            }
        }
    }

    private void moveView(float position) {
        mThumbPos = position;
        invalidate();
    }

    public static interface OnCheckedChangeListener {
        /**
         * Called when the checked state of a compound button has changed.
         *
         * @param buttonView The button view whose state has changed.
         * @param isChecked  The new checked state of buttonView.
         */
        void onCheckedChanged(View buttonView, boolean isChecked);
    }

    public static int dip2px(Context context, float dipValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dipValue * scale + 0.5f);
    }
}
